`include "define.v"
module alu (
    /*AUTOARG*/
   // Outputs
   valE, setcc, alucc,
   // Inputs
   icode, ifun, valC, valB, valA
   );

input   [3:0]   icode;
input   [3:0]   ifun;
input   [31:0]  valC;
input   [31:0]  valB;
input   [31:0]  valA;
output  [31:0]  valE;
output          setcc;
output  [2:0]   alucc;


wire [31:0] aluA = (icode == `IIRMOVL) ? valC :
                   (icode == `IRMMOVL) ? valC :
                   (icode == `IMRMOVL) ? valC :
                   valA;
                
wire [31:0] aluB = valB;

assign valE = (icode == `IRRMOVL) ? aluA :
              (icode == `IIRMOVL) ? aluA :
              (icode == `IRMMOVL) ? aluA + aluB :
              (icode == `IMRMOVL) ? aluA + aluB :
              (icode == `IOPL   ) ? ((ifun == `ALU_ADD) ? aluA + aluB : 
                                    (ifun == `ALU_SUB) ? aluB - aluA :
                                    (ifun == `ALU_AND) ? aluA & aluB :
                                    (ifun == `ALU_XOR) ? aluA ^ aluB :
                                    32'h0000_0000
                                    ) :
              (icode == `ICALL  ) ? aluB - 32'h4 :
              (icode == `IPUSHL ) ? aluB - 32'h4 :
              (icode == `IRET   ) ? aluB + 32'h4 : 
              (icode == `IPOPL  ) ? aluB + 32'h4 : 
              32'h0000_0000;

// Only IOPL instruction need set cc flags
wire setcc = (icode == `IOPL);

// Reference pipe.v
// ZF
assign alucc[2] = (valE == 32'h0000_0000);

// SF
assign alucc[1] = valE[31];

// OF
assign alucc[0] =
    ifun == `ALU_ADD ? 
        (aluA[31] == aluB[31]) & (aluA[31] != valE[31]) : 
    ifun == `ALU_SUB ? 
        (~aluA[31] == aluB[31]) & (aluB[31] != valE[31]) : 
    32'h0000_0000; 

endmodule

